#include "parser.h"
#include <QDebug>

Grammar::Grammar()
{
    grammar['S'][0] = "B";
    grammar['S'][1] = "C";
    grammar['S'][2] = "^"; // пусте правило
    grammar['B'][0] = "<O>R</F>S"; // секції конфігурації
    grammar['R'][0] = "DK";
    grammar['K'][0] = "/*E*/R";
    grammar['K'][1] = "^";
    grammar['C'][0] = "/*E*/S"; // коментарі
    grammar['D'][0] = "tD"; // t - всі термінальні символи, окрім "</" та "/*"
    grammar['D'][1] = "^";
    grammar['E'][0] = "eE"; // e - всі термінальні символи, окрім "*/"
    grammar['E'][1] = '^';
}

std::map<char, std::map<int, std::string> > &Grammar::getGrammar()
{
    return grammar;
}

void Grammar::__setSectionsName()
{
    for (uint i = 0; i < sections_list_name.size(); i++) {
        grammar['O'][i] = sections_list_name[i].toStdString();
        grammar['F'][i] = sections_list_name[i].toStdString();
    }
}

void Grammar::setSectionsName(const QStringList &sect_list)
{
    sections_list_name = sect_list;
    __setSectionsName();
}

namespace Parser
{

bool parser(QByteArray &buf, uint &buf_index, char nt_sym, Grammar &g_m, std::map<QString, std::map<QString, int> > &history_file_data, Parser::ParsingType pt)
{
    static std::map<QString, int> *curr_sect;
    static std::string sect_param; // містить поточний параметр опції
    uint buf_index_tmp = buf_index; // поточний індекс в буфері buf
    std::map<char, std::map<int, std::string> > &grammar = g_m.getGrammar();
    bool was_equal_vt_sym = false; // дійсний, якщо токен співпав з першим vt-символом в правилі
    for (uint i = 0; i < grammar[nt_sym].size(); i++) { // аналізуємо кожне правило нетермінального символа грамматики
        std::string sect_name; // містить назву секції
        if (was_equal_vt_sym) return false; // используєм свойство LL(1)-грамматики - правила начинаются с разних VT-символов.
                                           // підвищуємо швидкість роботи функції
        bool can_return = true;
        for (uint j = 0; j < grammar[nt_sym][i].size(); j++) { // аналізуємо кожний токен правила грамматики
            char sym = grammar[nt_sym][i][j];
            if (!isupper(sym)) // в правилі знайдено термінальний символ грамматики
            {
                if (buf_index > buf.size()) return false; // синтаксична помилка
                if ((nt_sym == 'O') || (nt_sym == 'F')) // токен - фіксоване слово. Правила 'O' та 'F' містять фіксовані слова.
                    // Назва секції
                {
                    if (isspace(buf[buf_index])) getBufIndexWithoutSpace(buf, ++buf_index);
                    if (buf[buf_index] == sym)
                    {
                        sect_name += buf[buf_index++];
                        continue;
                    }
                    can_return = false;
                    buf_index = buf_index_tmp;
                    break;
                }
                if (sym == 't') // обробка параметрів секцій
                {
                    if (isspace(buf[buf_index]))
                    {
                        if (sect_param.size() && (pt == Searching)) (*curr_sect)[sect_param.c_str()] = 1;
                        sect_param.clear();
                        getBufIndexWithoutSpace(buf, ++buf_index);
                    }
                    if (buf[buf_index] == '<') // перейде до пустого правила. За граматикою - всі символи окрім < та /*
                    {
                        if (sect_param.size() && (pt == Searching)) (*curr_sect)[sect_param.c_str()] = 1;
                    }
                    else if ((buf[buf_index] == '/') && (buf[buf_index + 1] == '*')) // коментар всередині секції конфігурації
                    {
                        if (sect_param.size() && (pt == Searching))
                        {
                            (*curr_sect)[sect_param.c_str()] = 1;
                            sect_param.clear();
                        }
                    }
                    else
                    {
                        sect_param += buf[buf_index++];
                        was_equal_vt_sym = true;
                        continue;
                    }
                }
                else if (sym == 'e') // обробка коментарів
                {
                    if ((buf[buf_index] == '*') && (buf[buf_index + 1] == '/')) ; // перейде до пустого правила
                    else
                    {
                        ++buf_index;
                        was_equal_vt_sym = true;
                        continue;
                    }
                }
                else if (sym == '^') // пусте правило
                {
                    if (nt_sym == 'S')
                        if (buf_index < (buf.size() - 1)) return false;
                    return true;
                }
                else if (isspace(buf[buf_index])) getBufIndexWithoutSpace(buf, ++buf_index);
                if (sym == buf[buf_index]) // токен співпадає з VT-символом у правилі граматики (< / > *)
                {
                    ++buf_index;
                    was_equal_vt_sym = true;
                    continue;
                }
            }
            else if (isupper(sym)) // в правилі знайдено нетермінальний символ грамматики
            {
                if (parser(buf, buf_index, sym, g_m, history_file_data, pt))
                {
                    was_equal_vt_sym = true;
                    continue;
                }
            }
            can_return = false;
            buf_index = buf_index_tmp;
            break;
        }
        if (can_return)
        {
            if (sect_name.size() && (nt_sym != 'F') && (pt == Searching)) // тільки для <sect_name>, а не для </sect_name>
            {
                sect_param.clear();
                curr_sect = &history_file_data[sect_name.c_str()];
            }
            return true;
        }
    }
    return false;
}

void getBufIndexWithoutSpace(QByteArray &buf, uint &buf_index)
{
    while (isspace(buf[buf_index])) ++buf_index;
}

}

